/*
 *
 *
 */
package BlueTooth;

import java.io.DataOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

// jsr082 API
import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DataElement;
import javax.bluetooth.DeviceClass;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.DiscoveryListener;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.RemoteDevice;
import javax.bluetooth.ServiceRecord;
import javax.bluetooth.UUID;

// midp/cldc API
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;

import DEF;
import GameView;
/**
 * Initialize BT device, search for BT services,
 * presents them to user and picks his/her choice,
 * @version ,
 */
public class BTClient implements Runnable, DiscoveryListener {
    /** Describes this server */
    private static final UUID SERVER_UUID =
        new UUID("F0E0D0C0B0A000908070605040302010", false);

    /** The attribute id of the record item with images names. */
    private static final int IMAGES_NAMES_ATTRIBUTE_ID = 0x4321;

    /** Shows the engine is ready to work. */
    private static final int READY = 0;

    /** Shows the engine is searching bluetooth devices. */
    private static final int DEVICE_SEARCH = 1;

    /** Shows the engine is searching bluetooth services. */
    private static final int SERVICE_SEARCH = 2;

    /** Keeps the current state of engine. */
    private int state = READY;

    /** Keeps the discovery agent reference. */
    private DiscoveryAgent discoveryAgent;

    /** Becomes 'true' when this component is finalized. */
    private boolean isClosed;

    /** Process the search/download requests. */
    private Thread processorThread;

    /** Collects the remote devices found during a search. */
    private Vector /* RemoteDevice */ devices = new Vector();

    /** Collects the services found during a search. */
    private Vector /* ServiceRecord */ records = new Vector();

    /** Keeps the device discovery return code. */
    private int discType;

    /** Keeps the services search IDs (just to be able to cancel them). */
    private int[] searchIDs;
    
    private String[] searchServerName;

    /** Keeps the image name to be load. */
    private String imageNameToLoad;

    /** Keeps the table of {name, Service} to process the user choice. */
    private Hashtable base = new Hashtable();

    /** Informs the thread the download should be canceled. */
    private boolean isDownloadCanceled;

    /** Optimization: keeps service search pattern. */
    private UUID[] uuidSet;

    /** Optimization: keeps attributes list to be retrieved. */
    private int[] attrSet;

    
    static public String infoStringComeServer;
    StreamConnection conn;
    String message;
    /**
     * Constructs the bluetooth server, but it is initialized
     * in the different thread to "avoid dead lock".
     */
  public  BTClient() {
        ///this.parent = parent;

    }

    public void start() {

        // we have to initialize a system in different thread...
        processorThread = new Thread(this);
        processorThread.start();
    }
    /**
     * Process the search/download requests.
     */
    public void run() {
        // initialize bluetooth first
        boolean isBTReady = false;

        try {
            // create/get a local device and discovery agent
            LocalDevice localDevice = LocalDevice.getLocalDevice();
            discoveryAgent = localDevice.getDiscoveryAgent();

            // remember we've reached this point.
            isBTReady = true;
        } catch (Exception e) {
        	
        	m_blueToothStatus = INIT_FAIL_BLUETOOTH;///
            if(DEF.DEBUG_IN_PHONE )
            {
            	GameView.s_debug_String = "Can't initialize bluetooth: ";
            }
            System.err.println("Can't initialize bluetooth: " + e);
        }

        // nothing to do if no bluetooth available
        if (!isBTReady) {
            return;
        }

        // initialize some optimization variables
        uuidSet = new UUID[1];

        // ok, we are interesting in btspp services only
        ///uuidSet[0] = new UUID(0x1101);

        // and only known ones, that allows pictures
        uuidSet[0] = SERVER_UUID;

        // we need an only service attribute actually
       /// attrSet = new int[1];

        // it's "images names" one
       /// attrSet[0] = IMAGES_NAMES_ATTRIBUTE_ID;

        setProcessConnectionStatus(SEARCH_SERVICE);
        // start processing the images search/download
        processConnection();
        
//        for (int i = 0; i < searchIDs.length; i++) {
//            discoveryAgent.cancelServiceSearch(searchIDs[i]);
//        }
        
        discoveryAgent.cancelInquiry(this);
        discoveryAgent =null;
    }

    /**
     * Invoked by system when a new remote device is found -
     * remember the found device.
     */
    public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) {
        // same device may found several times during single search
        if (devices.indexOf(btDevice) == -1) {
            devices.addElement(btDevice);
            if(DEF.DEBUG_IN_PHONE )
            {
            	try {
            	GameView.s_debug_String = "discover one device"+ btDevice.getFriendlyName(true);
            	//JDSE_String.drawString("discover one device", 0, 0, Graphics.LEFT | Graphics.TOP);
            	}
            	catch(java.io.IOException e)
            	{
            		GameView.s_debug_String = GameView.s_debug_String + " "+ e.toString();
            	}
            }
            System.out.println("discover one device:"+btDevice.getBluetoothAddress() );
        }
    }

    /**
     * Invoked by system when device discovery is done.
     * <p>
     * Remember the discType
     * and process its evaluation in another thread.
     */
    public void inquiryCompleted(int discType) {
        this.discType = discType;

        if(DEF.DEBUG_IN_PHONE )
        {
        	GameView.s_debug_String = "complete inquary ";
        }
        System.out.println("complete inquary ");
        synchronized (this) {
            notify();
        }
    }

    /**
     * Invoked by system when device discovery is done.
     */
    public void servicesDiscovered(int transID, ServiceRecord[] servRecord) {
        if(DEF.DEBUG_IN_PHONE )
        {
        	GameView.s_debug_String = "get server";
        }
    	 System.out.println("get server");
        for (int i = 0; i < servRecord.length; i++) {
            records.addElement(servRecord[i]);
        }
    }
    
    /**
     * Invoked by system when device discovery is done.
     */
    public void serviceSearchCompleted(int transID, int respCode) {
        // first, find the service search transaction index
        int index = -1;
        
       if(records.size()!=0)
       {
        if(DEF.DEBUG_IN_PHONE )
        {
        	GameView.s_debug_String = "serviceSearchCompleted";
        }
        System.out.println("serviceSearchCompleted");
       }
        for (int i = 0; i < searchIDs.length; i++) {
            if (searchIDs[i] == transID) {
                index = i;

                break;
            }
        }

        // error - unexpected transaction index
        if (index == -1) {
            System.err.println("Unexpected transaction index: " + transID);
            // process the error case here
        } else {
            searchIDs[index] = -1;
        }

        /*
         * Actually, we do not care about the response code -
         * if device is not reachable or no records, etc.
         */

        // make sure it was the last transaction
        for (int i = 0; i < searchIDs.length; i++) {
            if (searchIDs[i] != -1) {
                return;
            }
        }

        // ok, all of the transactions are completed
        synchronized (this) {
            notify();
        }
    }

    /** Sets the request to search the devices/services. */
    void requestSearch() {
        synchronized (this) {
            notify();
        }
    }

    /** Cancel's the devices/services search. */
    void cancelSearch() {
        synchronized (this) {
            if (state == DEVICE_SEARCH) {
                discoveryAgent.cancelInquiry(this);
            } else if (state == SERVICE_SEARCH) {
                for (int i = 0; i < searchIDs.length; i++) {
                    discoveryAgent.cancelServiceSearch(searchIDs[i]);
                }
            }
        }
    }

    /** Sets the request to load the specified image. */
    void requestLoad(String name) {
        synchronized (this) {
            imageNameToLoad = name;
            notify();
        }
    }

    /** Cancel's the image download. */
    void cancelLoad() {
        /*
         * The image download process is done by
         * this class's thread (not by a system one),
         * so no need to wake up the current thread -
         * it's running already.
         */
        isDownloadCanceled = true;
    }

    public void stop()
    {
    	destroy();
    }
    
    /**
     * Destroy a work with bluetooth - exits the accepting
     * thread and close notifier.
     */
    void destroy() {
        synchronized (this) {
            isClosed = true;
            isDownloadCanceled = true;
            notify();
        }

        // wait for acceptor thread is done
        try {
            processorThread.join();
        } catch (InterruptedException e) {
        } // ignore
    }
public int currentRecordIndex = -1;

public void setCurrentRecordIndex(int i)
{
	currentRecordIndex = i;
}

public void initConnection()
{
	try
	{
	if(currentRecordIndex!=-1)
	  {
		ServiceRecord sr=(ServiceRecord)records.elementAt(currentRecordIndex);
		String url = sr.getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);
		conn = (StreamConnection)Connector.open(url);
	  }
	}
	catch(Exception e)
	  {
		m_blueToothStatus = EXCEPTION_EXIT_BLUETOOTH ;
      if(DEF.DEBUG_IN_PHONE )
      {
      	GameView.s_debug_String = "BTClient InitConnection Error" + e.toString();
      }
      System.out.println("BTClient  InitConnection" + e.toString());
	  }

	
}


public void closeConnection()
{
	try
	{

	if(conn!=null)
		conn.close();
	}catch(Exception e)
	{
		if(DEF.DEBUG_IN_PHONE )
	      {
	      	GameView.s_debug_String = "BTClient closeConnection Error" + e.toString();
	      }
	      
	}
}
    public void sendMessage(String msg)
    {
    	message = msg;
    	Thread clientProcessThread = new Thread()
    	{
    		public void run()
    		{
    		try
    		{
    		 ///if(records.size() != 0)
    		if(currentRecordIndex!=-1)
    		  {
    			
    			///if(conn==null)
    			{
    			ServiceRecord sr=(ServiceRecord)records.elementAt(currentRecordIndex);
    			String url = sr.getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);
    			conn = (StreamConnection)Connector.open(url);
    			}
    			//send message
    			DataOutputStream dos = conn.openDataOutputStream();
    			dos.writeUTF(message);
    			System.out.println("CSend:"+message);
    			dos.flush();
    			dos.close();
    			//receive message
    			DataInputStream dis = conn.openDataInputStream();
    			infoStringComeServer =dis.readUTF();
    			dis.close();
    			System.out.println("CRecieve:"+infoStringComeServer);	
    			conn.close();
    		   }////record !=0
    		}
    		catch(Exception e)
    		  {
    			m_blueToothStatus = EXCEPTION_EXIT_BLUETOOTH ;
    	        if(DEF.DEBUG_IN_PHONE )
    	        {
    	        	GameView.s_debug_String = "BTClient SendMessage Error" + e.toString();
    	        }
    	        System.out.println("BTClient SM" + e.toString());
    		  }
    		}
    	};
    	clientProcessThread.start();
    }
    
    /**
     * Processes images search/download until component is closed
     * or system error has happen.
     */
    public int processConnectionStatus ;
    public final static int SEARCH_DEVICES =1;
    public final static int SEARCH_SERVICE =2;
    public final static int SUCCESS_CONNECTION =3;
    
    public void setProcessConnectionStatus(int s)
    {
    	processConnectionStatus = s;
    }
    
    private synchronized void processConnection() {
        while (!isClosed) {
            // wait for new search request from user
            state = READY;

//            try {
//                wait();
//            } catch (InterruptedException e) {
//                System.err.println("Unexpected interruption: " + e);
//
//                return;
//            }

            // check the component is destroyed
            if (isClosed) {
                return;
            }

            switch(processConnectionStatus)
            {
       
            case SEARCH_SERVICE:	
            if(m_blueToothStatus!=SERVICES_COMPLETED_BLUETOOTH)
            {
	            	// search for devices
	            if (!searchDevices()) {
	                return;
	            } else if (devices.size() == 0) {
	            	m_blueToothStatus = FOUND_NO_DEVICES_BLUETOOTH;///
	                continue;
	            }
	            
	            // search for services now
	            if (!searchServices()) {
	                return;
	            } else if (records.size() == 0) {
	            	m_blueToothStatus = FOUND_NO_SERVICES_BLUETOOTH;///
	                continue;  // no server find pls again 
	            }
            }
            m_blueToothStatus = SERVICES_COMPLETED_BLUETOOTH;
            //connect the server now
             
            
            // ok, something was found - present the result to user now
        //    if (!presentUserSearchResults()) {
        //        // services are found, but no names there
       //         continue;
       //     }
            break;
            case SUCCESS_CONNECTION:	
           
            while(true)
            {
            
            	 try {
                   wait();
               } catch (InterruptedException e) {
                   System.err.println("Unexpected interruption: " + e);

                   return;
               }
               
            	 if (isClosed) {
            		 m_blueToothStatus = SUCCESS_CLOSE_BLUETOOTH;///
                   return;
               }
            }
            ///
           }  
            // the several download requests may be processed
//            while (true) {
//                // this download is not canceled, right?
//                isDownloadCanceled = false;
//
//                // ok, wait for download or need to wait for next search
//                try {
//                    wait();
//                } catch (InterruptedException e) {
//                    System.err.println("Unexpected interruption: " + e);
//
//                    return;
//                }
//
//                // check the component is destroyed
//                if (isClosed) {
//                    return;
//                }
//
//
//                // this should never happen - monitor is taken...
//                if (isClosed) {
//                    return;
//                }
//
//                if (isDownloadCanceled) {
//                    continue; // may be next image to be download
//                }
//
//
//                // ok, show image to user
//             ////   parent.showImage(img, imageNameToLoad);
//
//                // may be next image to be download
//                continue;
//            }
        }
    }

    /**
     * Search for bluetooth devices.
     *
     * @return false if should end the component work.
     */
    private boolean searchDevices() {
        // ok, start a new search then
        state = DEVICE_SEARCH;
        devices.removeAllElements();

        try {
            discoveryAgent.startInquiry(DiscoveryAgent.GIAC, this);
        } catch (BluetoothStateException e) {
            if(DEF.DEBUG_IN_PHONE )
            {
            	GameView.s_debug_String = "Can't start inquiry now: " + e;
            }
            
            System.err.println("Can't start inquiry now: " + e);
          ////  parent.informSearchError("Can't start device search");

            return true;
        }

        try {
            wait(); // until devices are found
        } catch (InterruptedException e) {
            System.err.println("Unexpected interruption: " + e);

            return false;
        }

        // this "wake up" may be caused by 'destroy' call
        if (isClosed) {
            return false;
        }

        // no?, ok, let's check the return code then
        switch (discType) {
        case INQUIRY_ERROR:
         ///   parent.informSearchError("Device discovering error...");

        // fall through
        case INQUIRY_TERMINATED:
            // make sure no garbage in found devices list
            devices.removeAllElements();

            // nothing to report - go to next request
            break;

        case INQUIRY_COMPLETED:

            if (devices.size() == 0) {
            ///    parent.informSearchError("No devices in range");
            }

            // go to service search now
            break;

        default:
            // what kind of system you are?... :(
            if(DEF.DEBUG_IN_PHONE )
            {
            	GameView.s_debug_String = "system error:" + " unexpected device discovery code: " + discType;
            }
            System.err.println("system error:" + " unexpected device discovery code: " + discType);
            destroy();

            return false;
        }

        return true;
    }

    /**
     * Search for proper service.
     *
     * @return false if should end the component work.
     */
    
    public String getServerName(int index)
    {
       return  searchServerName[index];
    }
    
    public int getServerCount()
    {
    	return searchServerName.length;
    }
    
    private boolean searchServices() {
        state = SERVICE_SEARCH;
        records.removeAllElements();
        searchIDs = new int[devices.size()];

        searchServerName = new String[devices.size()];
        
        boolean isSearchStarted = false;
        int i;
        for ( i = 0; i < devices.size(); i++) {
            RemoteDevice rd = (RemoteDevice)devices.elementAt(i);

            try {
                searchIDs[i] = discoveryAgent.searchServices(null/*attrSet*/, uuidSet, rd, this);
            } catch (BluetoothStateException e) {
                if(DEF.DEBUG_IN_PHONE )
                {
                	GameView.s_debug_String = "Can't search services for: " + rd.getBluetoothAddress() +
                    " due to " + e;
                }
                System.err.println("Can't search services for: " + rd.getBluetoothAddress() +
                    " due to " + e);
                searchIDs[i] = -1;
                searchServerName[i]=null;
                continue;
            }
//            try
//            {
//            searchServerName[i]=""+rd.getFriendlyName(true);
//            }
//            catch(java.io.IOException e)
//            {
//            	searchServerName[i] = null;///"";
//            }
            isSearchStarted = true;
        }

        // at least one of the services search should be found
        if (!isSearchStarted) {
          ///  parent.informSearchError("Can't search services.");

            return true;
        }

        try {
            wait(); // until services are found  ,serviceSearchCompleted() call back
        } catch (InterruptedException e) {
            System.err.println("Unexpected interruption: " + e);

            return false;
        }

        // this "wake up" may be caused by 'destroy' call
        if (isClosed) {
            return false;
        }

        // actually, no services were found
        if (records.size() == 0) {
          ///  parent.informSearchError("No proper services were found");
        }
        else 
        {
        	for(i =0; i <records.size(); i++)
        	{
        		try
                {
        		ServiceRecord sr=(ServiceRecord)records.elementAt(i) ;
        	    RemoteDevice rd= sr.getHostDevice();
                searchServerName[i]=""+rd.getFriendlyName(true);
                }
                catch(java.io.IOException e)
                {
                	searchServerName[i] = null;///"";
                }
        	}
        	
        }

        return true;
    }
    
    public int m_blueToothStatus ;
    public  static final int  INIT_FAIL_BLUETOOTH = 1;
   public static final int   FOUND_NO_DEVICES_BLUETOOTH  = 2;
  public  static final int    FOUND_NO_SERVICES_BLUETOOTH = 3;
  public  static final int   SERVICES_COMPLETED_BLUETOOTH  = 4;   
  public static final int   SUCCESS_CLOSE_BLUETOOTH  = 5;   
    
  public static final int   EXCEPTION_EXIT_BLUETOOTH  = 6;   
    
    public int getBlueToothStatus()
    {
    	return m_blueToothStatus;  //INIT_FAIL_BLUETOOTH
    	
    }

    /**
     * Gets the collection of the images titles (names)
     * from the services, prepares a hashtable to match
     * the image name to a services list, presents the images names
     * to user finally.
     *
     * @return false if no names in found services.
     */
//    private boolean presentUserSearchResults() {
//        base.clear();
//
//        for (int i = 0; i < records.size(); i++) {
//            ServiceRecord sr = (ServiceRecord)records.elementAt(i);
//
//            // get the attribute with images names
//            DataElement de = sr.getAttributeValue(IMAGES_NAMES_ATTRIBUTE_ID);
//
//            if (de == null) {
//                System.err.println("Unexpected service - missed attribute");
//
//                continue;
//            }
//
//            // get the images names from this attribute
//            Enumeration deEnum = (Enumeration)de.getValue();
//
//            while (deEnum.hasMoreElements()) {
//                de = (DataElement)deEnum.nextElement();
//
//                String name = (String)de.getValue();
//
//                // name may be stored already
//                Object obj = base.get(name);
//
//                // that's either the ServiceRecord or Vector
//                if (obj != null) {
//                    Vector v;
//
//                    if (obj instanceof ServiceRecord) {
//                        v = new Vector();
//                        v.addElement(obj);
//                    } else {
//                        v = (Vector)obj;
//                    }
//
//                    v.addElement(sr);
//                    obj = v;
//                } else {
//                    obj = sr;
//                }
//
//                base.put(name, obj);
//            }
//        }
//
//        return false;
//       /// return parent.showImagesNames(base);
//    }

  
} // end of class 'BTClient' definition
